//
//  UIImageAdditions.m
//  Tint
//
//  Created by Wu Kevin on 9/28/12.
//
//

#import "UIImageAdditions.h"

@implementation UIImage (Tint)


#pragma mark - Stretchable image

- (UIImage *)stretchableImage
{
    return [self stretchableImageWithCenterSize:CGSizeMake(1.0, 1.0)];
}

- (UIImage *)stretchableImageWithCenterSize:(CGSize)centerSize
{
    CGFloat hSpacing = floor((self.size.width - centerSize.width)/2.0);
    CGFloat vSpacing = floor((self.size.height - centerSize.height)/2.0);
    UIEdgeInsets edgeInsets = UIEdgeInsetsMake(vSpacing, hSpacing, vSpacing, hSpacing);
    return [self stretchableImageWithCapInsets:edgeInsets];
}

- (UIImage *)stretchableImageWithCapInsets:(UIEdgeInsets)capInsets
{
    UIEdgeInsets edgeInsets = UIEdgeInsetsMake(floor(capInsets.top),
                                               floor(capInsets.left),
                                               floor(capInsets.bottom),
                                               floor(capInsets.right));
    
    UIDevice *device = [UIDevice currentDevice];
    if ( [device.systemVersion floatValue] < 5.0 ) {
        return [self stretchableImageWithLeftCapWidth:edgeInsets.left topCapHeight:edgeInsets.top];
    }
    
    return [self resizableImageWithCapInsets:edgeInsets];
}


#pragma mark - Round corner

- (UIImage *)makeRoundCornersOfSize:(CGSize)cornerSize
{
    return [self makeRoundCornersOfSize:cornerSize usingMode:TTImageRoundCornerModeAll];
}

- (UIImage *)makeRoundCornersOfSize:(CGSize)cornerSize usingMode:(TTImageRoundCornerMode)mode
{
    if ((round(cornerSize.width) <= 0.0) ||
        (round(cornerSize.height) <= 0.0))
    {
        return self;
    }
    
    CGSize newCornerSize = CGSizeMake(round(cornerSize.width),
                                      round(cornerSize.height));
    
    CGFloat width = self.size.width;
    CGFloat height = self.size.height;
    
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(NULL,
                                                 width,
                                                 height,
                                                 8,
                                                 4 * width,
                                                 colorSpace,
                                                 kCGImageAlphaPremultipliedFirst);
	CGColorSpaceRelease(colorSpace);
    
    CGContextBeginPath(context);
    CGContextSaveGState(context);
    CGContextTranslateCTM(context, 0.0, 0.0);
    CGContextScaleCTM(context, newCornerSize.width, newCornerSize.height);
    CGFloat fw = width / newCornerSize.width;
    CGFloat fh = height / newCornerSize.height;
    if ( mode == TTImageRoundCornerModeLeft ) {
        CGContextMoveToPoint(context, fw/2.0, fh);
        CGContextAddArcToPoint(context, 0.0, fh, 0.0, fh/2.0, 1.0);
        CGContextAddArcToPoint(context, 0.0, 0.0, fw/2.0, 0.0, 1.0);
        CGContextAddLineToPoint(context, fw, 0.0);
        CGContextAddLineToPoint(context, fw, fh);
        CGContextAddLineToPoint(context, fw/2.0, fh);
    } else if ( mode == TTImageRoundCornerModeTop ) {
        CGContextMoveToPoint(context, fw, fh/2.0);
        CGContextAddArcToPoint(context, fw, fh, fw/2.0, fh, 1.0);
        CGContextAddArcToPoint(context, 0.0, fh, 0.0, fh/2.0, 1.0);
        CGContextAddLineToPoint(context, 0.0, 0.0);
        CGContextAddLineToPoint(context, fw, 0.0);
        CGContextAddLineToPoint(context, fw, fh/2.0);
    } else if ( mode == TTImageRoundCornerModeRight ) {
        CGContextMoveToPoint(context, fw/2.0, 0.0);
        CGContextAddArcToPoint(context, fw, 0.0, fw, fh/2.0, 1.0);
        CGContextAddArcToPoint(context, fw, fh, fw/2.0, fh, 1.0);
        CGContextAddLineToPoint(context, 0.0, fh);
        CGContextAddLineToPoint(context, 0.0, 0.0);
        CGContextAddLineToPoint(context, fw/2.0, 0.0);
    } else if ( mode == TTImageRoundCornerModeBottom ) {
        CGContextMoveToPoint(context, 0.0, fh/2.0);
        CGContextAddArcToPoint(context, 0.0, 0.0, fw/2.0, 0.0, 1.0);
        CGContextAddArcToPoint(context, fw, 0.0, fw, fh/2.0, 1.0);
        CGContextAddLineToPoint(context, fw, fh);
        CGContextAddLineToPoint(context, 0.0, fh);
        CGContextAddLineToPoint(context, 0.0, fh/2.0);
    } else if ( mode == TTImageRoundCornerModeLeftTop ) {
        CGContextMoveToPoint(context, fw/2.0, fh);
        CGContextAddArcToPoint(context, 0.0, fh, 0.0, fh/2.0, 1.0);
        CGContextAddLineToPoint(context, 0.0, 0.0);
        CGContextAddLineToPoint(context, fw, 0.0);
        CGContextAddLineToPoint(context, fw, fh);
        CGContextAddLineToPoint(context, fw/2.0, fh);
    } else if ( mode == TTImageRoundCornerModeTopRight ) {
        CGContextMoveToPoint(context, fw, fh/2.0);
        CGContextAddArcToPoint(context, fw, fh, fw/2.0, fh, 1.0);
        CGContextAddLineToPoint(context, 0.0, fh);
        CGContextAddLineToPoint(context, 0.0, 0.0);
        CGContextAddLineToPoint(context, fw, 0.0);
        CGContextAddLineToPoint(context, fw, fh/2.0);
    } else if ( mode == TTImageRoundCornerModeRightBottom ) {
        CGContextMoveToPoint(context, fw/2.0, 0.0);
        CGContextAddArcToPoint(context, fw, 0.0, fw, fh/2.0, 1.0);
        CGContextAddLineToPoint(context, fw, fh);
        CGContextAddLineToPoint(context, 0.0, fh);
        CGContextAddLineToPoint(context, 0.0, 0.0);
        CGContextAddLineToPoint(context, fw/2.0, 0.0);
    } else if ( mode == TTImageRoundCornerModeBottomLeft ) {
        CGContextMoveToPoint(context, 0.0, fh/2.0);
        CGContextAddArcToPoint(context, 0.0, 0.0, fw/2.0, 0.0, 1.0);
        CGContextAddLineToPoint(context, fw, 0.0);
        CGContextAddLineToPoint(context, fw, fh);
        CGContextAddLineToPoint(context, 0.0, fh);
        CGContextAddLineToPoint(context, 0.0, fh/2.0);
    } else {
        CGContextMoveToPoint(context, fw, fh/2.0);
        CGContextAddArcToPoint(context, fw, fh, fw/2.0, fh, 1.0);
        CGContextAddArcToPoint(context, 0.0, fh, 0.0, fh/2.0, 1.0);
        CGContextAddArcToPoint(context, 0.0, 0.0, fw/2.0, 0.0, 1.0);
        CGContextAddArcToPoint(context, fw, 0.0, fw, fh/2.0, 1.0);
    }
    CGContextClosePath(context);
    CGContextRestoreGState(context);
    CGContextClip(context);
	
    
    CGContextDrawImage(context, CGRectMake(0.0, 0.0, width, height), self.CGImage);
    
    
    CGImageRef clippedImageRef = CGBitmapContextCreateImage(context);
    UIImage *clippedImage = [UIImage imageWithCGImage:clippedImageRef]; // AUTO
    CGImageRelease(clippedImageRef);
    
    CGContextRelease(context);
	
    return clippedImage;
}



#pragma mark - Cropping

- (UIImage *)cropCenter
{
    CGFloat width = MIN(self.size.width, self.size.height);
    return [self cropToSize:CGSizeMake(width, width) usingMode:TTImageCropModeCenter];
}

- (UIImage *)cropToSize:(CGSize)newSize
{
	return [self cropToSize:newSize usingMode:TTImageCropModeTopLeft];
}

- (UIImage *)cropToSize:(CGSize)newSize usingMode:(TTImageCropMode)mode
{
    CGFloat width = self.size.width;
    CGFloat height = self.size.height;
    
	CGFloat x = 0.0;
    CGFloat y = 0.0;
    
    if ( mode == TTImageCropModeTopCenter ) {
        x = (width - newSize.width) * 0.5;
        y = 0.0;
    } else if ( mode == TTImageCropModeTopRight ) {
        x = width - newSize.width;
        y = 0.0;
    } else if ( mode == TTImageCropModeBottomLeft ) {
        x = 0.0;
        y = height - newSize.height;
    } else if ( mode == TTImageCropModeBottomCenter ) {
        x = newSize.width * 0.5;
        y = height - newSize.height;
    } else if ( mode == TTImageCropModeBottomRight ) {
        x = width - newSize.width;
        y = height - newSize.height;
    } else if ( mode == TTImageCropModeLeftCenter ) {
        x = 0.0;
        y = (height - newSize.height) * 0.5;
    } else if ( mode == TTImageCropModeRightCenter ) {
        x = width - newSize.width;
        y = (height - newSize.height) * 0.5;
    } else if ( mode == TTImageCropModeCenter ) {
        x = (width - newSize.width) * 0.5;
        y = (height - newSize.height) * 0.5;
    } else {
        x = 0.0;
        y = 0.0;
    }
    
    return [self cropAt:CGRectMake(x, y, newSize.width, newSize.height)];
}

- (UIImage *)cropAt:(CGRect)rect
{
    CGRect newRect = CGRectMake(round(rect.origin.x),
                                round(rect.origin.y),
                                round(rect.size.width),
                                round(rect.size.height));
    CGImageRef croppedImageRef = CGImageCreateWithImageInRect(self.CGImage, newRect);
    UIImage *croppedImage = [UIImage imageWithCGImage:croppedImageRef]; // AUTO
    CGImageRelease(croppedImageRef);
    return croppedImage;
}



#pragma mark - Scaling

- (UIImage *)scaleToFitSize:(CGSize)bounds
{
    CGFloat factor = MIN( (bounds.width/self.size.width), (bounds.height/self.size.height) );
    return [self scaleByFactor:factor];
}

- (UIImage *)scaleByFactor:(CGFloat)factor
{
    return [self scaleToSize:CGSizeMake(self.size.width * factor, self.size.height * factor)];
}

- (UIImage *)scaleToSize:(CGSize)newSize
{
    CGFloat width = round(newSize.width);
    CGFloat height = round(newSize.height);
    
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
	CGContextRef context = CGBitmapContextCreate(NULL,
                                                 width,
                                                 height,
                                                 8,
                                                 4 * width,
                                                 colorSpace,
                                                 kCGImageAlphaPremultipliedFirst);
	CGColorSpaceRelease(colorSpace);
    
    
    CGContextDrawImage(context, CGRectMake(0.0, 0.0, width, height), self.CGImage);
    
    
	CGImageRef scaledImageRef = CGBitmapContextCreateImage(context);
	UIImage *scaledImage = [UIImage imageWithCGImage:scaledImageRef]; // AUTO
	CGImageRelease(scaledImageRef);
    
    CGContextRelease(context);
    
    return scaledImage;
}



#pragma mark - Merging

- (UIImage *)mergeWithImage:(UIImage *)image
{
    return [self mergeWithImage:image atPoint:CGPointZero];
}

- (UIImage *)mergeAtCenterWithImage:(UIImage *)image
{
    CGFloat x = (self.size.width - image.size.width) / 2.0;
    CGFloat y = (self.size.height - image.size.height) / 2.0;
    return [self mergeWithImage:image atPoint:CGPointMake(x, y)];
}

- (UIImage *)mergeWithImage:(UIImage *)image atPoint:(CGPoint)point
{
    CGFloat width = self.size.width;
    CGFloat height = self.size.height;
    
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
	CGContextRef context = CGBitmapContextCreate(NULL,
                                                 width,
                                                 height,
                                                 8,
                                                 4 * width,
                                                 colorSpace,
                                                 kCGImageAlphaPremultipliedFirst);
	CGColorSpaceRelease(colorSpace);
    
    
    CGContextDrawImage(context, CGRectMake(0.0, 0.0, width, height), self.CGImage);
    CGContextDrawImage(context, CGRectMake(round(point.x), round(point.y), image.size.width, image.size.height), image.CGImage);
    
    
	CGImageRef mergedImageRef = CGBitmapContextCreateImage(context);
	UIImage *mergedImage = [UIImage imageWithCGImage:mergedImageRef]; // AUTO
	CGImageRelease(mergedImageRef);
    
    CGContextRelease(context);
    
    return mergedImage;
}


#pragma mark - Masking

- (UIImage *)maskWithImage:(UIImage *)image
{
    CGImageRef sourceImageRef = self.CGImage;
    CGImageRef maskImageRef = image.CGImage;
    
	CGImageRef maskRef = CGImageMaskCreate(CGImageGetWidth(maskImageRef),
                                           CGImageGetHeight(maskImageRef),
                                           CGImageGetBitsPerComponent(maskImageRef),
                                           CGImageGetBitsPerPixel(maskImageRef),
                                           CGImageGetBytesPerRow(maskImageRef),
                                           CGImageGetDataProvider(maskImageRef),
                                           NULL,
                                           false);
    
    CGImageRef maskedRef = CGImageCreateWithMask(sourceImageRef, maskRef);
	UIImage *resultImage = [UIImage imageWithCGImage:maskedRef]; // AUTO
	CGImageRelease(maskedRef);
    
    CGImageRelease(maskRef);
	
	return resultImage;
}


#pragma mark - Filtering

- (UIImage *)grayscale
{
	CGFloat width = self.size.width;
	CGFloat height = self.size.height;
    
	CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
	CGContextRef context = CGBitmapContextCreate(NULL,
                                                 width,
                                                 height,
                                                 8,
                                                 3 * width,
                                                 colorSpace,
                                                 kCGImageAlphaNone);
	CGColorSpaceRelease(colorSpace);
    
	// Image quality
	CGContextSetShouldAntialias(context, false);
	CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
    
	// Draw the image in the bitmap context
    CGContextDrawImage(context, CGRectMake(0.0, 0.0, width, height), self.CGImage);
    
	// Create an image object from the context
	CGImageRef grayscaleImageRef = CGBitmapContextCreateImage(context);
	UIImage *grayscaleImage = [UIImage imageWithCGImage:grayscaleImageRef]; // AUTO
	CGImageRelease(grayscaleImageRef);
    
    CGContextRelease(context);
    
	return grayscaleImage;
}

- (UIImage *)negative
{
    CGFloat width = self.size.width;
    CGFloat height = self.size.height;
    
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
	CGContextRef context = CGBitmapContextCreate(NULL,
                                                 width,
                                                 height,
                                                 8,
                                                 4 * width,
                                                 colorSpace,
                                                 kCGImageAlphaPremultipliedFirst);
	CGColorSpaceRelease(colorSpace);
    
    
    CGContextSetBlendMode(context, kCGBlendModeCopy);
    
    CGContextDrawImage(context, CGRectMake(0.0, 0.0, width, height), self.CGImage);
    
    CGContextSetBlendMode(context, kCGBlendModeDifference);
    CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
    CGContextFillRect(context, CGRectMake(0.0, 0.0, width, height));
    
    
	CGImageRef negativeImageRef = CGBitmapContextCreateImage(context);
	UIImage *negativeImage = [UIImage imageWithCGImage:negativeImageRef]; // AUTO
	CGImageRelease(negativeImageRef);
    
    CGContextRelease(context);
    
    return negativeImage;
}


@end
